#include "stdio.h"
#include "defs.h"
#include "sched.h"

#define Kernel_Page            0x80210000
#define LOW_MEMORY             0x80211000
#define PAGE_SIZE              4096UL


struct task_struct* current;
struct task_struct* task[NR_TASKS];


extern int task_test_done;
extern void __init_sepc(void);

//If next==current,do nothing; else update current and call __switch_to.
void switch_to(struct task_struct* next) 
{
    /*your code*/
    puts("enter function switch_to\n");
    if(next != current)
    {
    	struct task_struct* old_current = current;
    	current = next;
    	__switch_to(old_current, next);
    }
}


int task_init_done = 0;
//initialize tasks, set member variables
void task_init(void) {
    puts("task init...\n");
	//puts("enter function task_init\n");
    //initialize task[0]
    current = (struct task_struct*) Kernel_Page;
    current->state = TASK_RUNNING;
    current->counter = 0;
    current->priority = 5;
    current->blocked = 0;
    current->pid = 0;

    task[0] = current;
    task[0]->thread.sp = (unsigned long long) task[0] + TASK_SIZE;
	puts("finish init task 0 \n");
    
    //set other 4 tasks
    for (unsigned char i = 1; i <= LAB_TEST_NUM; ++i) 
    {
        /*your code*/
        puts("enter task initializing loop\n");
        struct task_struct* task_p = (task+i);
        task_p->state = TASK_RUNNING;
		task_p->counter = 0;
		task_p->priority = 5;
		task_p->blocked = 0;
		task_p->pid = i;
		task_p->thread.sp = Kernel_Page + i*PAGE_SIZE;
		task_p->thread.ra = __init_sepc;
        printf("[PID = %d] Process Create Successfully!\n", task_p->pid);
    }
    task_init_done = 1;
}


#ifdef SJF 
//simulate the cpu timeslice, which means a short time frame that gets assigned to process for CPU execution
void do_timer(void) {
    if (!task_init_done) return;
    if (task_test_done) return;
    printf("[*PID = %d] Context Calculation: counter = %d,priority = %d\n", current->pid, current->counter, current->priority);
    //current process's counter -1, judge whether to schedule or go on.
    /*your code*/
    if(current->counter == 0)
    {
    	schedule();
    }
    else
    {
    	(current->counter)--;
    }
}

//Select the next task to run. If all tasks are done(counter=0), set task0's counter to 1 and it would 
//assign new test case.
void schedule(void) {
    unsigned char next; 
    /*your code*/
    puts("enter schedule\n");
	long sj_cnt = INT64_MAX; 
	for(int i=1; i<=LAB_TEST_NUM; i++)
	{
		struct task_struct* task_p = (task+i);
		if((task_p ->counter) <= sj_cnt)
		{
			sj_cnt = task_p->counter;
			next = i;
		}
	}
	
	struct task_struct* next_task_p = (task + next);
	
    if((current->pid) != (next_task_p->pid))
    {
        printf("[ %d -> %d ] Switch from task %d[%lx] to task %d[%lx], prio: %d, counter: %d\n", 
        current->pid,next_task_p->pid,
        current->pid, (unsigned long)(current->thread.sp), 
        next_task_p->pid, (unsigned long)(next_task_p->thread.sp), 
        next_task_p->priority, next_task_p->counter);
    }
    
    switch_to(next_task_p);
}
#endif


#ifdef PRIORITY
//simulate the cpu timeslice, which measn a short time frame that gets assigned to process for CPU execution
void do_timer(void) 
{
    if (!task_init_done) return;
    if (task_test_done) return;
    printf("[*PID = %d] Context Calculation: counter = %d,priority = %d\n", 
    		current->pid, 
    		current->counter, 
    		current->priority);

    //current process's counter -1, judge whether to schedule or go on.
    /*your code*/
    if(current->counter == 0)
    {
    	schedule();
    }
    else
    {
    	(current->counter)--;
    }
}

//Select the next task to run. If all tasks are done(counter=0), set task0's counter to 1 and it would 
//assign new test case.
void schedule(void) {
    unsigned char next; 
    /*your code*/
    long pri_cnt = -1; 
    
	for(int i=1; i<= LAB_TEST_NUM; ++i)
	{
		struct task_struct* next_task_p = (task + i);
		if(((next_task_p->priority) > pri_cnt) and (next_task_p->counter !=0))
		{
			next = i;
		}
	}
	
	struct task_struct* next_task_p = (task + i);
	if(next == 0)
	{
		task->counter = 1;
	}
	
    if(current->pid != next_task_p->pid)
    {
        printf("[ %d -> %d ] Switch from task %d[%lx] to task %d[%lx], prio: %d, counter: %d\n",
        		current->pid,
        		task[next]->pid,
        		current->pid,
        		(unsigned long)current->thread.sp,
        		next_task_p->pid,
        		(unsigned long)next_task_p->thread.sp,
        		next_task_p->priority,
        		next_task_p->counter);
    }
    switch_to(current, next_task_p);
}
#endif
